/*
 * 版权所有(C)，上海海鼎信息工程股份有限公司，lizhixing，所有权利保留。
 * <p>
 * 项目名：	boot-demo
 * 文件名：	RestExceptionHandler
 * 模块说明：
 * 修改历史：
 * 2022/5/24 - lizhixing - 创建。
 */
package com.lovely602.boot.bootdemo.exception;

import static java.lang.String.format;

import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.lovely602.boot.bootdemo.domain.common.Constant;
import com.lovely602.boot.bootdemo.domain.common.RestResponse;
import com.lovely602.boot.bootdemo.domain.common.exception.BusinessException;
import com.lovely602.boot.bootdemo.domain.common.exception.ErrorCode;
import com.lovely602.boot.bootdemo.utils.JsonUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 异常控制处理
 * 
 * @author lizhixing
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.lovely626.boot.bootdemo.controller")
public class RestExceptionHandler {

  @ResponseBody
  @ResponseStatus(value = HttpStatus.OK)
  @ExceptionHandler(Throwable.class)
  public RestResponse<Void> restExceptionHandler(HttpServletRequest request, Exception e) {
    RestResponse<Void> response = buildResponse(e);
    print(request, e, response);
    return response;
  }

  private void print(HttpServletRequest req, Exception ex, RestResponse<Void> response) {
    if (ex instanceof BusinessException) {
      log.warn(format("请求%s失败", req.getRequestURI()), ex);
      log.warn(format("响应:/n%s", JsonUtil.objectToJson(response)));
      String message = ((BusinessException) ex).getLogMessage();
      if (StringUtils.isNotBlank(message)) {
        log.warn(format("message:/n%s", ((BusinessException) ex).getLogMessage()));
      }
    } else {
      log.error(format("请求%s失败", req.getRequestURI()), ex);
      log.error(format("响应:/n%s", JsonUtil.objectToJson(response)));
    }
    logRequestInfo(req);
  }

  protected void logRequestInfo(HttpServletRequest request) {

    Enumeration<String> headerNames = request.getHeaderNames();
    Map<String, Collection<String>> headers = new HashMap<>();
    while (headerNames.hasMoreElements()) {
      String key = headerNames.nextElement();
      headers.put(key, Collections.list(request.getHeaders(key)));
    }

    String sb = "\r\n===========================================================================\r\n"
        + //
        "inbound Message: " + "\r\n" + //
        "traceid: " + MDC.get("trace_id") + "\r\n" + //
        "url: " + request.getRequestURL().toString() + "\r\n" + //
        "querys: " + request.getQueryString() + "\r\n" + //
        "charset: " + request.getCharacterEncoding() + "\r\n" + //
        "method: " + request.getMethod() + "\r\n" + //
        "headers: " + getHeaders(headers) + "\r\n" + //
        "client: " + request.getRemoteAddr() + "\r\n" + //
        "===========================================================================";//
    log.info(sb);
  }

  private String getHeaders(Map<String, Collection<String>> headers) {
    StringBuilder sb = new StringBuilder();

    for (Map.Entry<String, Collection<String>> header : headers.entrySet()) {
      sb.append(header.getKey()).append(":").append(header.getValue()).append(" | ");
    }

    return sb.toString();
  }

  private RestResponse<Void> buildResponse(Throwable e) {
    RestResponse<Void> response;
    if (e instanceof IllegalArgumentException) {
      response = RestResponse.fail(ErrorCode.ILLEGAL_ARGUMENT_EXCEPTION, e.getLocalizedMessage());
    } else if (e instanceof MethodArgumentNotValidException) {
      StringBuilder errorMsg = new StringBuilder();
      BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
      List<ObjectError> errors = bindingResult.getAllErrors();
      for (ObjectError error : errors) {
        if (error instanceof FieldError) {
          errorMsg.append(((FieldError) error).getField()).append(error.getDefaultMessage())
              .append(Constant.SEMICOLON_ENGLISH);
        }
      }
      response = RestResponse.fail(ErrorCode.METHOD_ARGUMENT_NOT_VALID_EXCEPTION,
          errorMsg.toString());
    } else if (e instanceof BusinessException) {
      response = RestResponse.fail((BusinessException) e);
    } else {
      response = RestResponse.fail(ErrorCode.UNKNOWN_BUSINESS_EXCEPTION);
    }
    return response;
  }

  private RestResponse<Void> decodeTimeoutException(String message) {
    return RestResponse.fail(ErrorCode.TIMEOUT_EXCEPTION);
  }
}
